package com.ikas.ai.server.module.data.service;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.ikas.ai.consts.Consts;
import com.ikas.ai.consts.MachineAndWorkConditionConst;
import com.ikas.ai.server.error.BusinessException;
import com.ikas.ai.server.module.InitRedis;
import com.ikas.ai.server.module.data.dao.DataMeteDao;
import com.ikas.ai.server.module.data.dao.MeteDao;
import com.ikas.ai.server.module.data.model.BaseMete;
import com.ikas.ai.server.module.data.model.DataMete;
import com.ikas.ai.server.module.data.model.dto.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Description: mete测点服务类
 * @Author: qi.yilai
 * @Date: 2023/2/2 17:36
 * @Version: 1.0.0
 **/
@Service
@Slf4j
public class MeteService extends ServiceImpl<MeteDao, BaseMete> {

    @Autowired
    private MeteDao meteDao;
    @Autowired
    private DataMeteDao dataMeteDao;

    @Autowired
    private InitRedis initRedis;

    /**
     * 批量保存设备测点
     *
     * @param metes
     * @return
     */
    public List<BaseMete> saveOrUpdateMete(List<BaseMete> metes) {
        if (CollectionUtils.isNotEmpty(metes)) {
            for (BaseMete mete : metes) {
                if (Objects.isNull(mete.getId())) {
                    mete.setCreateTime(new Date());
                } else {
                    mete.setUpdateTime(new Date());
                }
            }
        } else {
            throw new BusinessException("新增测点集合数据不能为空!");
        }
        saveOrUpdateBatch(metes, metes.size());
        return metes;
    }

    /**
     * 保存设备测点
     *
     * @param meteAddUpdateDTO
     * @param userId
     */
    public BaseMete saveOrUpdateMete(MeteAddUpdateDTO meteAddUpdateDTO, Long userId) {
        BaseMete mete = new BaseMete();
        BeanUtils.copyProperties(meteAddUpdateDTO, mete);
        mete.setType(meteAddUpdateDTO.getType());
        if (Objects.isNull(mete.getId())) {
            mete.setCreateTime(new Date());
            mete.setCreator(userId);
            mete.setUpdater(userId);
        } else {
            mete.setUpdateTime(new Date());
            mete.setUpdater(userId);
        }
        saveOrUpdate(mete);
        return mete;
    }

    /**
     * 根据机组编号获取设备列表
     *
     * @param machineNo
     * @return
     */
    public List<SimpleBaseMeteDTO> getMetesByMachineNo(String machineNo) {
        LambdaQueryWrapper<BaseMete> meteLambdaQueryWrapper = new LambdaQueryWrapper<>();
        meteLambdaQueryWrapper.select(BaseMete::getId, BaseMete::getMeteCode, BaseMete::getSignalCnName)
                .eq(StringUtils.isNotBlank(machineNo), BaseMete::getMachineNo, machineNo);
        List<BaseMete> meteList = list(meteLambdaQueryWrapper);
        return meteList.stream().map(item -> {
            SimpleBaseMeteDTO simpleBaseMeteDTO = new SimpleBaseMeteDTO();
            BeanUtils.copyProperties(item, simpleBaseMeteDTO);
            return simpleBaseMeteDTO;
        }).collect(Collectors.toList());
    }

    /**
     * 获取所有遥测数据
     *
     * @return
     */
    public List<BaseMete> getBaseMete() {
        LambdaQueryWrapper<BaseMete> meteLambdaQueryWrapper = new LambdaQueryWrapper<>();
        return list(meteLambdaQueryWrapper);
    }

    /**
     * 根据设备编号获取设备列表
     *
     * @param equipmentCode
     * @return
     */
    public List<BaseMete> getMetesByEquipmentCode(String equipmentCode) {
        LambdaQueryWrapper<BaseMete> meteLambdaQueryWrapper = new LambdaQueryWrapper<>();
        meteLambdaQueryWrapper.eq(BaseMete::getEquipmentCode, equipmentCode);
        return list(meteLambdaQueryWrapper);
    }

    public List<BaseMete> queryMetes(String meteCode, String meteId, String signalCnName, String systemNo, String machineNo, String equipmentCode, Integer meteType, Long oid, String parentNodeName, String nodeName, String type, Integer common) {
        if (StringUtils.isNotBlank(meteId)) {
            Pattern compile = Pattern.compile(Consts.REGEX);
            if (!compile.matcher(meteId).matches()) {
                throw new BusinessException("测点id必须为数字");
            }
        }
        LambdaQueryChainWrapper<BaseMete> lambdaQueryWrapper = new LambdaQueryChainWrapper<>(meteDao);
        lambdaQueryWrapper.like(StringUtils.isNotBlank(meteCode), BaseMete::getMeteCode, meteCode);
        lambdaQueryWrapper.eq(StringUtils.isNotBlank(meteId), BaseMete::getMeteId, StringUtils.isNotBlank(meteId) ? Long.valueOf(meteId) : null);
        lambdaQueryWrapper.like(StringUtils.isNotBlank(signalCnName), BaseMete::getSignalCnName, signalCnName);
        lambdaQueryWrapper.eq(StringUtils.isNotBlank(systemNo), BaseMete::getSystemNo, systemNo);
        if (Objects.nonNull(common) && common == 1) {
            //查询指定机组和common机组
            lambdaQueryWrapper.in(StringUtils.isNotBlank(machineNo), BaseMete::getMachineNo, machineNo, MachineAndWorkConditionConst.COMMON_MACHINE_NO);
        } else {
            lambdaQueryWrapper.eq(StringUtils.isNotBlank(machineNo), BaseMete::getMachineNo, machineNo);
        }
        lambdaQueryWrapper.eq(StringUtils.isNotBlank(equipmentCode), BaseMete::getEquipmentCode, equipmentCode);
        lambdaQueryWrapper.eq(Objects.nonNull(oid), BaseMete::getOid, oid);
        lambdaQueryWrapper.eq(Objects.nonNull(meteType), BaseMete::getMeteType, meteType);
        lambdaQueryWrapper.like(StringUtils.isNotBlank(parentNodeName), BaseMete::getParentNodeName, parentNodeName);
        lambdaQueryWrapper.like(StringUtils.isNotBlank(nodeName), BaseMete::getNodeName, nodeName);
        lambdaQueryWrapper.eq(StringUtils.isNotBlank(type), BaseMete::getType, type);
        lambdaQueryWrapper.orderByDesc(BaseMete::getCreateTime);
        return lambdaQueryWrapper.list();
    }

    public MeteAddDTO add(MeteAddDTO meteAddDTO, Long userId) {
        List<BaseMete> metes = new ArrayList<>(selectByMeteName(meteAddDTO.getMeteCode()));
        if (CollectionUtils.isNotEmpty(metes)) {
            throw new BusinessException("该变量名称已存在！");
        }
        List<BaseMete> meteIdMetes = selectByMeteId(meteAddDTO.getMeteId());
        if (CollectionUtils.isNotEmpty(meteIdMetes)) {
            throw new BusinessException("该测点Id已存在！");
        }
        List<BaseMete> oidMetes = selectByOid(meteAddDTO.getOid());
        if (CollectionUtils.isNotEmpty(oidMetes)) {
            throw new BusinessException("该oid已存在！");
        }
        BaseMete mete = new BaseMete();
        BeanUtils.copyProperties(meteAddDTO, mete);
        if (1 == meteAddDTO.getMeteType()) {
            mete.setUpperUpperLimit(Objects.nonNull(meteAddDTO.getUpperUpperLimit()) ? Double.valueOf(meteAddDTO.getUpperUpperLimit()) : null);
            mete.setUpperLimit(Objects.nonNull(meteAddDTO.getUpperLimit()) ? Double.valueOf(meteAddDTO.getUpperLimit()) : null);
            mete.setLowerLowerLimit(Objects.nonNull(meteAddDTO.getLowerLowerLimit()) ? Double.valueOf(meteAddDTO.getLowerLowerLimit()) : null);
            mete.setLowerLimit(Objects.nonNull(meteAddDTO.getLowerLimit()) ? Double.valueOf(meteAddDTO.getLowerLimit()) : null);
            mete.setUpperRange(Objects.nonNull(meteAddDTO.getUpperRange()) ? Double.valueOf(meteAddDTO.getUpperRange()) : null);
            mete.setLowerRange(Objects.nonNull(meteAddDTO.getLowerRange()) ? Double.valueOf(meteAddDTO.getLowerRange()) : null);
        }
        mete.setValidMeteValue(Objects.nonNull(meteAddDTO.getValidMeteValue()) ? Integer.valueOf(meteAddDTO.getValidMeteValue()) : null);
        mete.setType(meteAddDTO.getType());
        mete.setCreateTime(new Date());
        mete.setUpdateTime(new Date());
        mete.setCreator(userId);
        mete.setUpdater(userId);
        save(mete);
        initRedis.initBaseMeteCacheAfterAdd(Collections.singletonList(mete));
        return meteAddDTO;
    }

    private List<BaseMete> selectByMeteId(Long meteId) {
        LambdaQueryChainWrapper<BaseMete> lambdaQueryChainWrapper = new LambdaQueryChainWrapper<>(meteDao);
        return lambdaQueryChainWrapper.eq(BaseMete::getMeteId, meteId).list();
    }
/*
    public BaseMete selectByMeteCode(String meteCode) {
        LambdaQueryChainWrapper<BaseMete> lambdaQueryChainWrapper = new LambdaQueryChainWrapper<>(meteDao);
        List<BaseMete> list = lambdaQueryChainWrapper.eq(BaseMete::getMeteCode, meteCode).list();

        return CollectionUtils.isEmpty(list) ? null : list.get(0);
    }*/


    public BaseMete selectByMachNoMeteCodeMeteId(String machineNo, String meteCode, Long meteId) {
        LambdaQueryChainWrapper<BaseMete> lambdaQueryChainWrapper = new LambdaQueryChainWrapper<>(meteDao);
        List<BaseMete> list = lambdaQueryChainWrapper
                .eq(StringUtils.isNotBlank(machineNo), BaseMete::getMachineNo, machineNo)
                .eq(StringUtils.isNotBlank(meteCode), BaseMete::getMeteCode, meteCode)
                .eq(Objects.nonNull(meteId), BaseMete::getMeteId, meteId)
                .list();
        return CollectionUtils.isEmpty(list) ? null : list.get(0);
    }

    private List<BaseMete> selectByOid(Long oid) {
        LambdaQueryChainWrapper<BaseMete> lambdaQueryChainWrapper = new LambdaQueryChainWrapper<>(meteDao);
        return lambdaQueryChainWrapper.eq(BaseMete::getOid, oid).list();
    }

    public MeteUpdateDTO update(MeteUpdateDTO meteUpdateDTO, Long userId) {
        BaseMete source = meteDao.selectById(meteUpdateDTO.getId());
        if (Objects.isNull(source)) {
            throw new BusinessException("该变量不存在！");
        }
        List<BaseMete> metes = new ArrayList<>(selectByMeteName(meteUpdateDTO.getMeteCode()));
        if (CollectionUtils.isNotEmpty(metes) && !source.getId().equals(metes.get(0).getId())) {
            throw new BusinessException("该变量名称已存在！");
        }
        List<BaseMete> meteIdMetes = selectByMeteId(meteUpdateDTO.getMeteId());
        if (CollectionUtils.isNotEmpty(meteIdMetes) && !source.getId().equals(meteIdMetes.get(0).getId())) {
            throw new BusinessException("该测点Id已存在！");
        }
        List<BaseMete> oidMetes = selectByOid(meteUpdateDTO.getOid());
        if (CollectionUtils.isNotEmpty(oidMetes) && !source.getId().equals(oidMetes.get(0).getId())) {
            throw new BusinessException("该oid已存在！");
        }
        BeanUtils.copyProperties(meteUpdateDTO, source);
        if (1 == meteUpdateDTO.getMeteType()) {
            source.setUpperUpperLimit(Objects.nonNull(meteUpdateDTO.getUpperUpperLimit()) ? Double.valueOf(meteUpdateDTO.getUpperUpperLimit()) : null);
            source.setUpperLimit(Objects.nonNull(meteUpdateDTO.getUpperLimit()) ? Double.valueOf(meteUpdateDTO.getUpperLimit()) : null);
            source.setLowerLowerLimit(Objects.nonNull(meteUpdateDTO.getLowerLowerLimit()) ? Double.valueOf(meteUpdateDTO.getLowerLowerLimit()) : null);
            source.setLowerLimit(Objects.nonNull(meteUpdateDTO.getLowerLimit()) ? Double.valueOf(meteUpdateDTO.getLowerLimit()) : null);
            source.setUpperRange(Objects.nonNull(meteUpdateDTO.getUpperRange()) ? Double.valueOf(meteUpdateDTO.getUpperRange()) : null);
            source.setLowerRange(Objects.nonNull(meteUpdateDTO.getLowerRange()) ? Double.valueOf(meteUpdateDTO.getLowerRange()) : null);
        }
        source.setValidMeteValue(Objects.nonNull(meteUpdateDTO.getValidMeteValue()) ? Integer.valueOf(meteUpdateDTO.getValidMeteValue()) : null);
        source.setUpdateTime(new Date());
        source.setUpdater(userId);
        updateById(source);
        initRedis.initBaseMeteCacheAfterUpdate(Collections.singletonList(source));
        return meteUpdateDTO;
    }

    public Set<BaseMete> selectByMeteName(String meteName) {
        LambdaQueryChainWrapper<BaseMete> lambdaQueryChainWrapper = new LambdaQueryChainWrapper<>(meteDao);
        return lambdaQueryChainWrapper.eq(BaseMete::getMeteCode, meteName).list().stream().filter(distinctByKey(BaseMete::getMeteCode)).collect(Collectors.toSet());
    }

    /**
     * 通过excel批量导入测点
     *
     * @param file
     * @param userId
     * @param index
     */
    public void importMetes(MultipartFile file, Long userId, Integer index) throws Exception {
        ImportParams importParams = new ImportParams();
        //设置从第几个（索引）sheet开始
        importParams.setStartSheetIndex(index);
        List<MeteExcelDTO> dtoList = ExcelImportUtil.importExcel(file.getInputStream(), MeteExcelDTO.class, importParams);
        log.info("导入的测点数据:{}", JSON.toJSONString(dtoList));
        //根据meteName去重
        dtoList = dtoList.stream().filter(i -> Objects.nonNull(i.getMeteCode())).filter(distinctByKey(MeteExcelDTO::getMeteCode)).collect(Collectors.toList());
        LambdaQueryChainWrapper<BaseMete> lambdaQueryChainWrapper = new LambdaQueryChainWrapper<>(meteDao);
        List<BaseMete> all = lambdaQueryChainWrapper.list();
        Map<String, BaseMete> meteMap = all.stream().collect(Collectors.toMap(BaseMete::getMeteCode, i -> i));
        //将测点分为更新和保存两组
        Map<Boolean, List<MeteExcelDTO>> map = dtoList.stream().collect(Collectors.partitioningBy(i -> meteMap.containsKey(i.getMeteCode())));
        //更新的测点集合
        List<MeteExcelDTO> updateList = map.get(true);
        //保存的测点集合
        List<MeteExcelDTO> saveList = map.get(false);

        List<BaseMete> saves = saveList.stream().map(meteExcelDTO -> {
            BaseMete mete = new BaseMete();
            BeanUtils.copyProperties(meteExcelDTO, mete);
            mete.setCreator(userId);
            mete.setUpdater(userId);
            mete.setCreateTime(new Date());
            mete.setUpdateTime(new Date());
            return mete;
        }).collect(Collectors.toList());
        saveBatch(saves);
        initRedis.initBaseMeteCacheAfterAdd(saves);

        List<BaseMete> updates = updateList.stream().map(meteExcelDTO -> {
            BaseMete mete = meteMap.get(meteExcelDTO.getMeteCode());
            BeanUtils.copyProperties(meteExcelDTO, mete);
            mete.setUpdater(userId);
            mete.setUpdateTime(new Date());
            return mete;
        }).collect(Collectors.toList());
        updateBatchById(updates);
        initRedis.initBaseMeteCacheAfterUpdate(updates);
    }

    private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    /**
     * 根据测点名称获取测点
     *
     * @param meteNames
     * @return
     */
    public List<BaseMete> queryMetesByMeteName(String machineNo, List<String> meteNames) {
        LambdaQueryWrapper<BaseMete> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(BaseMete::getMeteCode, meteNames).eq(BaseMete::getMachineNo, machineNo);
        return list(lambdaQueryWrapper);
    }

    public List<BaseMete> queryMetesByMeteName(List<String> meteNames) {
        LambdaQueryWrapper<BaseMete> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(BaseMete::getMeteCode, meteNames);
        return list(lambdaQueryWrapper);
    }

    public Map<String, List<String>> isUsed(Long id) {
        BaseMete mete = meteDao.selectById(id);
        LambdaQueryChainWrapper<DataMete> wrapper = new LambdaQueryChainWrapper<>(dataMeteDao);
        List<DataMete> list = wrapper.eq(DataMete::getMeteId, mete.getMeteId())
                .or().eq(DataMete::getMeteCode, mete.getMeteCode())
                .list();
        Map<String, List<String>> map = new HashMap<>();
        list.forEach(dataMete -> {
            List<String> values = map.get(dataMete.getMachineNo());
            if (CollectionUtils.isEmpty(values)) {
                map.put(dataMete.getMachineNo(), Lists.newArrayList(dataMete.getWfName()));
            } else {
                values.add(dataMete.getWfName());
                map.put(dataMete.getMachineNo(), values);
            }
        });
        map.forEach((k, v) -> map.put(k, v.stream().distinct().collect(Collectors.toList())));
        return map;
    }

    public List<SimpleBaseMeteDTO> getMetesByType(String machineNo, Integer meteType, Integer common) {
        LambdaQueryChainWrapper<BaseMete> lambdaQueryChainWrapper = new LambdaQueryChainWrapper<>(meteDao);
        List<BaseMete> list;
        if (Objects.nonNull(common) && common == 1) {
            //查询机组和common
            list = lambdaQueryChainWrapper.select(BaseMete::getId, BaseMete::getMeteCode, BaseMete::getSignalCnName, BaseMete::getMachineNo, BaseMete::getMeteId)
                    .in(StringUtils.isNotBlank(machineNo), BaseMete::getMachineNo, machineNo, MachineAndWorkConditionConst.COMMON_MACHINE_NO)
                    .eq(Objects.nonNull(meteType), BaseMete::getMeteType, meteType)
                    .list();
        } else {
            list = lambdaQueryChainWrapper.select(BaseMete::getId, BaseMete::getMeteCode, BaseMete::getSignalCnName, BaseMete::getMachineNo, BaseMete::getMeteId)
                    .eq(StringUtils.isNotBlank(machineNo), BaseMete::getMachineNo, machineNo)
                    .eq(Objects.nonNull(meteType), BaseMete::getMeteType, meteType)
                    .list();
        }
        return list.stream().map(item -> {
            SimpleBaseMeteDTO simpleBaseMeteDTO = new SimpleBaseMeteDTO();
            BeanUtils.copyProperties(item, simpleBaseMeteDTO);
            return simpleBaseMeteDTO;
        }).collect(Collectors.toList());
    }

    public List<SimpleBaseMeteDTO> getMetesByMachineNoAndUcommon(String machineNo) {
        LambdaQueryWrapper<BaseMete> meteLambdaQueryWrapper = new LambdaQueryWrapper<>();
        meteLambdaQueryWrapper.select(BaseMete::getId, BaseMete::getMeteCode, BaseMete::getSignalCnName)
                .in(StringUtils.isNotBlank(machineNo), BaseMete::getMachineNo, machineNo, MachineAndWorkConditionConst.COMMON_MACHINE_NO);
        List<BaseMete> meteList = list(meteLambdaQueryWrapper);
        return meteList.stream().map(item -> {
            SimpleBaseMeteDTO simpleBaseMeteDTO = new SimpleBaseMeteDTO();
            BeanUtils.copyProperties(item, simpleBaseMeteDTO);
            return simpleBaseMeteDTO;
        }).collect(Collectors.toList());
    }
}
